/** PettyXML - A very basic xml parser
  * Copyright (c) 2008 Philipp Gildein (rmbl@openspeak-project.org)
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *
  *    * Redistributions of source code must retain the above copyright notice,
  *         this list of conditions and the following disclaimer.
  *    * Redistributions in binary form must reproduce the above copyright notice,
  *         this list of conditions and the following disclaimer in the documentation
  *         and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */

#ifndef __PETTYXML_HPP__
#define __PETTYXML_HPP__

#include <string>
#include <fstream>
#include <map>
#include <vector>
#include <exception>

typedef unsigned short int ushort;

/**	\class XMLException
 * 	\brief A very basic exception class
 *
 * 	The XMLException class provides just one method except of the
 *  (de-)constructors and allows the user to easily get the error
 *  message after it got thrown by a part of NanoXML.
 */
class XMLException : public std::exception
{
 public:
 	/** \brief The constructor of the XMLException class
 	 *  \param msg The error message
 	 */
	XMLException(const std::string &msg) : mMsg(msg) { }

	/** \brief The destructor of the XMLException class */
	virtual ~XMLException(void) throw()  { }

	/** \brief Returnes the error messages
	 *  \return The error message
	 */
	virtual const char* what(void) const throw() { return mMsg.c_str(); }
	
 protected:
 	std::string mMsg;	/**< The error message */
};


/** \class XMLNode
 *  \brief Contains all properties of a specific node in a xml document
 *
 *  The XMLNode represents one specific node in a xml document.
 *  It can parse its own node including all attributes and children and
 *  can write them back to the file.
 * 	The public methods allow easy access to all attributes and children
 *  and allows to change them or add new ones.
 */
class XMLNode
{
 public:
 	/** \brief The Type of the node */
	enum Type
	{
		UNKNOWN = 0,
		ELEMENT,
		COMMENT,
		DEFINITION,
		DTD,
		TEXT
	};

 public:
	typedef std::map<std::string, std::string> AttributesMap;
	typedef std::vector<XMLNode*> ChildrenVector;
	typedef ChildrenVector::const_iterator 	const_iterator;
	typedef ChildrenVector::iterator		iterator;

	friend class XMLDocument;

 	/** \brief The constructor of the XMLNode class */
	XMLNode(void);
	
	/** \brief The constructor of the XMLNode class
	 * 	\param name The name of the node
	 */
	XMLNode(const std::string &name);

	/** \brief The destructor of the XMLNode class */
	virtual ~XMLNode(void);

	/** \brief Set the value of an attribute
	 *  \param name The name of the attribute to change
	 * 	\param value The new value of the attribute
	 */
	void setAttribute(const std::string &name, const std::string &value);

	/** \brief Get the value of an attribute
	 * 	\param name The name of the attribute
	 * 	\return The value of the attribute
	 */
	std::string getAttribute(const std::string &name) const;

	/** \brief Check if an attribute exists
	 * 	\param name The name of the attribute
	 * 	\return True if it does exist
	 */
	bool attributeExists(const std::string &name) const;

	/** \brief Set the name of the node
	 * 	\param name The new name of the node
	 */
	inline void setName(const std::string &name) { mName = name; }

	/** \brief Get the name of the node
	 * 	\return The name of the node
	 */
	inline std::string getName(void) const { return mName; }

	/** \brief Set the text inside of the starting and closing tags
	 * 	\param text The text to use
	 */
	inline void setText(const std::string &text) { mText = text; if (mType != TEXT) mType = TEXT; }

	/** \brief Get the text inside of the tag
	 * 	\return The text inside of the tag
	 */
	inline std::string getText(void) const { return mText; }

	/** \brief Get the %Type of the node
	 * 	\return The %Type of the node
	 */
	inline Type getType(void) const { return mType; }

	/** \brief Add a child tag to the node
	 * 	\param child The child to add
	 */
	inline void addChild(XMLNode *child) { if (child) mChildren.push_back(child); }

	/** \brief Remove a child from the node
	 * 	\param child The child to remove
	 */
	void removeChild(XMLNode *child);

	/** \brief Remove a child from the node
	 * 	\param index The index (0..?) of the child
	 */
	void removeChild(ushort index);

	/** \brief Get a pointer to a child
	 * 	\param index The index (0..?) of the child
	 * 	\return The child or 0
	 */
	XMLNode* getChild(ushort index) const { return index < mChildren.size() ? mChildren[index] : 0; }

	/** \brief Get the first child in the vector
	 * 	\return The first child in the vector or 0
	 */
	inline XMLNode* getFirstChild(void) const { return mChildren.size() ? mChildren[0] : 0; }

	/** \brief Find a specific child
	 * 	\param name The name of the child
	 * 	\return The child or 0
	 */
	XMLNode* findChild(const std::string &name) const;

	/** \brief Return an iterator to the beginning of the vector
	 * 	\return The iterator to the beginning of the vector
	 */
	iterator begin(void) { return mChildren.begin(); }

	/** \brief Return a const_iterator to the beginning of the vector
	 * 	\return The const_iterator to the beginning of the vector
	 */
	const_iterator begin(void) const { return mChildren.begin(); }

	/** \brief Return an iterator to the end of the vector
	 * 	\return The iterator to the end of the vector
	 */
	iterator end(void) { return mChildren.end(); }

	/** \brief Return a const_iterator to the end of the vector
	 * 	\return The const_iterator to the end of the vector
	 */
	const_iterator end(void) const { return mChildren.end(); }

 protected:
 	/** \brief Parse a part of an xml file
 	 * 	\param restOfFile The rest of the file to parse
 	 * 	\return True if it got parsed successfully
 	 */
	bool parse(std::string &restOfFile);

	/** \brief Print the node to the given file
	 * 	\param file The file to write to
	 * 	\param col The column this node is in
	 */
	void printToFile(std::ostream &file, const ushort &col) const;

	/** \brief Try to identify a tag by its beginning
	 * 	\param tag The tag to identify
	 * 	\return The %Type of the tag
	 */
	Type identify(const std::string &tag) const;

	/** \brief Parse a given tag
	 * 	\param tag The tag to parse
	 * 	\return The %Type of the tag
	 */
	Type parseTag(const std::string &tag);
 
 	AttributesMap 	mAttributes;	/**< Map with all attributes of the node */
 	ChildrenVector	mChildren;		/**< Vector with all children of the node */
 	std::string		mName;			/**< The name of the node */
 	std::string		mText;			/**< The text inside of the node if there is any */
 	Type			mType;			/**< The %Type of the node */
 	bool			mClosed;		/**< Has the parser found the closing tag already? */

};


/** \class XMLDocument
 * 	\brief The basic xml document
 *
 * 	The XMLDocument class represents a xml file and interacts with it.
 * 	It also inherits the XMLNode class to allow access to the root
 * 	nodes of this document and the possibility to change them or add
 * 	new ones.
 */
class XMLDocument : public XMLNode
{
 public:
 	/** \brief The constructor of the XMLDocument class
 	 * 	\param file The xml file to read/write from/to
 	 */
 	XMLDocument(const std::string &file);

 	/** \brief The constructor of the XMLDocument class */
 	XMLDocument(void);

 	/** \brief The destructor of the XMLDocument class */
	virtual ~XMLDocument(void);

	/** \brief Change the name of the xml file
	 * 	\param file The new filename
	 */
	void setFile(const std::string &file) { mFile = file; }

	/** \brief Parse the contents of the file
	 * 	\return True if the parsing is successfull
	 */
	bool parse(void);

	/** \brief Parse the contents of the string
	 * 	\param xml The xml string
	 * 	\return True if the parsing is successfull
	 */
	bool parse(const std::string &xml);

	/** \brief Save everything back to the file
	 * 	\return True if everything got saved successfully
	 */
	bool save(void);

	/** \brief Save everything to a stream
	 * 	\param out The stream to save to
	 * 	\return True if everything got saved successfully
	 */
	bool save(std::ostream &out);

 protected:	
 	std::string 	mFile;		/**< The name of the file to read/write from/to */
};

#endif
